#pragma once
#include <strmif.h>

class CMemAllocator : public IMemAllocator
{
    CMemAllocator();
    virtual ~CMemAllocator();
    
    CMemAllocator(const CMemAllocator&);
    CMemAllocator& operator=(const CMemAllocator&);
    
public:

    static HRESULT CreateInstance(IMemAllocator**);

    //IUnknown

    HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**);
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();
    
    //IMemAllocator

    HRESULT STDMETHODCALLTYPE SetProperties( 
        ALLOCATOR_PROPERTIES*,
        ALLOCATOR_PROPERTIES*);
    
    HRESULT STDMETHODCALLTYPE GetProperties( 
        ALLOCATOR_PROPERTIES*);
    
    HRESULT STDMETHODCALLTYPE Commit();
    
    HRESULT STDMETHODCALLTYPE Decommit();
    
    HRESULT STDMETHODCALLTYPE GetBuffer( 
        IMediaSample**,
        REFERENCE_TIME*,
        REFERENCE_TIME*,
        DWORD);
    
    HRESULT STDMETHODCALLTYPE ReleaseBuffer(IMediaSample*);
    
private:

    ULONG m_cRef;
    HANDLE m_hCond;
    HANDLE m_hMutex;
    ALLOCATOR_PROPERTIES m_props;
    bool m_bCommitted;
    long m_cActive;
    
    class Sample;
    Sample* m_samples;    
    HRESULT allocate_sample();
    IMediaSample* get_sample();
    
    class Lock 
    {
        Lock(const Lock&);
        Lock& operator=(const Lock&);
        
    public:
    
        Lock();
        ~Lock();
        
        HRESULT Seize(CMemAllocator*);
        void Release();
        
    private:
    
        HANDLE m_hMutex;
        
    };

    class Sample : public IMediaSample
    {
        Sample(CMemAllocator*);
        Sample(const Sample&);

        virtual ~Sample();
        
        Sample& operator=(const Sample&);

    public:
    
        static HRESULT Create(CMemAllocator*, Sample*&);                        
        static void Destroy(Sample*);        

        CMemAllocator* const m_pAllocator;
        ULONG m_cRef;
        Sample* m_pNext;        
        
        void Init();
        //allocator is committed, and sample is being moved
        //from pool and given to caller of GetBuffer; its
        //internal buffer has already been allocated
        
        void Final();
        //allocator is commited, and sample is being moved
        //back into pool via ReleaseBuffer; its internal buffer
        //is preserved
        
        HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**);
        ULONG STDMETHODCALLTYPE AddRef();
        ULONG STDMETHODCALLTYPE Release();
        
        //IMediaSample interface:

        HRESULT STDMETHODCALLTYPE GetPointer( 
            BYTE** ppBuffer);
        
        long STDMETHODCALLTYPE GetSize();
        
        HRESULT STDMETHODCALLTYPE GetTime( 
            REFERENCE_TIME* pTimeStart,
            REFERENCE_TIME* pTimeEnd);
        
        HRESULT STDMETHODCALLTYPE SetTime( 
            REFERENCE_TIME* pTimeStart,
            REFERENCE_TIME* pTimeEnd);
        
        HRESULT STDMETHODCALLTYPE IsSyncPoint();
        
        HRESULT STDMETHODCALLTYPE SetSyncPoint( 
            BOOL bIsSyncPoint);
        
        HRESULT STDMETHODCALLTYPE IsPreroll();
        
        HRESULT STDMETHODCALLTYPE SetPreroll( 
            BOOL bIsPreroll);
        
        long STDMETHODCALLTYPE GetActualDataLength();
        
        HRESULT STDMETHODCALLTYPE SetActualDataLength(long);
        
        HRESULT STDMETHODCALLTYPE GetMediaType( 
            AM_MEDIA_TYPE** ppMediaType);
        
        HRESULT STDMETHODCALLTYPE SetMediaType( 
            AM_MEDIA_TYPE* pMediaType);
        
        HRESULT STDMETHODCALLTYPE IsDiscontinuity();
        
        HRESULT STDMETHODCALLTYPE SetDiscontinuity( 
            BOOL bDiscontinuity);
        
        HRESULT STDMETHODCALLTYPE GetMediaTime( 
            LONGLONG* pTimeStart,
            LONGLONG* pTimeEnd);
        
        HRESULT STDMETHODCALLTYPE SetMediaTime( 
            LONGLONG* pTimeStart,
            LONGLONG* pTimeStop);
            
    private:

        bool m_sync_point;
        bool m_preroll;
        bool m_discontinuity;
        __int64 m_start_time;
        __int64 m_stop_time;
        __int64 m_media_start_time;
        __int64 m_media_stop_time;
        long m_actual_data_length;  //what portion of allocated memory holds actual data
        
        BYTE* m_buf;
        long m_buflen;  //how much memory was allocated 
        long m_off;     //to satisfy alignment requirements
        
        AM_MEDIA_TYPE* m_pmt;

    };

};
